﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using System.Text.RegularExpressions;
using PBDesk.Utilities;
using PBDesk.DAL;
using System.Data;
using System.Configuration;

namespace PBDesk.AutoGen.AutoEnums
{
    public class EnumCreator
    {
        #region Constants
        //public string ASM_NAMESPACE = ConfigurationManager.AppSettings["defaultNamespace"];
        //public string OUTPUT_ASM_FILE_NAME = ConfigurationManager.AppSettings["assemblyFileName"];
        //public string OUTPUT_DIR = AppDomain.CurrentDomain.BaseDirectory + "..\\..\\..\\Outputs\\";

        #endregion

        public static void BuildEnum(ModuleBuilder moduleBuilder, EnumCreationList list)
        {
            if (moduleBuilder != null && list != null && list.EnumList.Count > 0)
            {
                for (int i = 0; i < list.EnumList.Count; i++)
                {
                    EnumItem item = list.EnumList[i];
                    BuildEnum(moduleBuilder, item.EnumName, item.TableName, item.ValueColumn, item.Literalcolumn, Type.GetType(item.EnumType));
                }
            }
        }

        public static void BuildEnum(ModuleBuilder moduleBuilder, string enumName, string tableName, string valueColumn, string literalColumn, Type underlyingType)
        {
            if (moduleBuilder != null)
            {
                if (underlyingType == null)
                    underlyingType = typeof(int);

                if (enumName.IsNotNullAndNotEmpty() && tableName.IsNotNullAndNotEmpty() && valueColumn.IsNotNullAndNotEmpty() && literalColumn.IsNotNullAndNotEmpty())
                {
                    List<EnumFormat> list = GetEnumListFromDB(tableName, valueColumn, literalColumn, underlyingType);

                    if (list != null && list.Count > 0)
                    {
                        string qualifiedEnumName = ConfigurationManager.AppSettings["defaultNamespace"] + "." + enumName;
                        EnumBuilder enumBuilder = moduleBuilder.DefineEnum(qualifiedEnumName, TypeAttributes.Public, underlyingType);
                        foreach (EnumFormat item in list)
                        {
                            enumBuilder.DefineLiteral(NormalizeLiteralNames(item.EnumLiteral), item.EnumValue);
                        }
                        enumBuilder.CreateType();
                    }
                }
            }
        }

        private static List<EnumFormat> GetEnumListFromDB(string tableName, string valueColumn, string literalColumn, Type underlyingType)
        {
            string commandText = GenerateCommandText(tableName, valueColumn, literalColumn);
            List<EnumFormat> list = new List<EnumFormat>();
            object enumValue = null;
            string enumLiteral;
            using (IDataReader reader = DBManager.ExecuteDataReader(commandText))
            {
                while (reader.Read())
                {
                    if (underlyingType == typeof(int))
                        enumValue = (int)DBHelper.GetValueFromDataReader<int>(reader, "ValueColumn");
                    else if (underlyingType == typeof(short))
                        enumValue = (short)DBHelper.GetValueFromDataReader<short>(reader, "ValueColumn");
                    else if (underlyingType == typeof(long))
                        enumValue = (long)DBHelper.GetValueFromDataReader<long>(reader, "ValueColumn");
                    else if (underlyingType == typeof(System.UInt16))
                        enumValue = (UInt16)DBHelper.GetValueFromDataReader<UInt16>(reader, "ValueColumn");
                    else if (underlyingType == typeof(System.UInt32))
                        enumValue = (UInt32)DBHelper.GetValueFromDataReader<UInt32>(reader, "ValueColumn");
                    else if (underlyingType == typeof(System.UInt64))
                        enumValue = (UInt64)DBHelper.GetValueFromDataReader<UInt64>(reader, "ValueColumn");
                    else
                        throw new Exception("Invalid datatype to create Enum");

                    enumLiteral = (string)DBHelper.GetValueFromDataReader<string>(reader, "LiteralColumn");

                    list.Add(new EnumFormat(enumValue, enumLiteral));
                }
                
            }

            return list;
        }

        private static string GenerateCommandText(string tableName, string valueColumn, string literalColumn)
        {
            return string.Format("Select {0} as \"ValueColumn\", {1} as \"LiteralColumn\" From {2} Where IsActive = 1", valueColumn, literalColumn, tableName);
        }

        private static string NormalizeLiteralNames(string enumValueName)
        {
            // Remove invalid characters from everywhere and replace them with_   
            var level1Correction = Regex.Replace(enumValueName, "[^A-Za-z0-9]+", "_");

            // Make sure the name does not begin with a number. if it does add an _ to the beginning   
            var number = 0;
            var beginsWithNumber = int.TryParse(level1Correction[0].ToString(), out number);
            return beginsWithNumber ? level1Correction.Insert(0, "_") : level1Correction;
        }
    }
}
